const fs = require('fs');
const RClass = require('./../utils/R.js');
const task = require('./../module/task');
const callCmd = require('./../utils/callCmd/index');
const checkFail = require('./../utils/checkFail');
const table = 'task';
const {
    task_index
} = require('./../utils/keyword.js');

let taskMethods = callCmd({
    db: null,
    table: null,
    defaultCallBack(R,err,ret) {
        if (err) {
            R.setError(err)
                .resCallBack();
        } else {
            if (ret instanceof Array) {
                ret = ret.map(_ => new task().set(_));
            }
            R.resCallBack(ret);
        }
    },
    defaultAction(R,dbMethod,obj){
        this.db[dbMethod]({
            table: this.table,
            ...obj
        },this.defaultCallBack.bind(this,R));
    },
    // 这里直接为 getLimit(R)
    getLimit({ content: { param } }) {
        let page = parseInt(param.page);
        let size = parseInt(param.size);
        page = page.toString() === (param.page || '').toString() ? page : false;
        size = size.toString() === (param.size || '').toString() ? size : false;
        if (page && size) {
            return ` limit ${page * size},${size}`
        } else {
            return '';
        }
    },
    // 虽然好像不好将 sql 拼接的事情暴露
    orderBy({ content: { param }}) {
        if ('orderby' in param) {
            return param.orderby;
        } else {
            return 'order by id desc';
        }
    },
    // 请求对象中 api 直接为 以下方法名称
    getWhere(str) {
        if (str) {
            return `where ${str} and task_index="${task_index}"`;
        } else {
            return `where task_index="${task_index}"`;
        }
    },

    // 获取所有任务，包括完成和未完成
    getAllTask(db,R) {
        this.defaultAction.bind(this,R,'query',{
            sql: `select * from ${table} ${this.getWhere()} ${this.orderBy(R)} ${this.getLimit(R)}`
        })();
    },
    // 获取所有未完成的任务，其中 log 部分只是一个文件名
    getRuning(db,R) {
        this.defaultAction.bind(this,R,'query',{
            sql: `select * from ${table}  ${this.getWhere('schedule<100 and schedule>=0')} ${this.orderBy(R)} ${this.getLimit(R)}`
        })();
    },
    // 获取所有已经完成的任务，log 部分可能还是一个文件名
    getRunOver(db,R) {
        this.defaultAction.bind(this,R,'query',{
            sql: `select * from ${table} ${this.getWhere('schedule>=100')} ${this.orderBy(R)} ${this.getLimit(R)}`
        })();
    },
    // 获取所有失败的任务，log 不在是文件
    getFail(db,R) {
        this.defaultAction.bind(this,R,'query',{
            sql: `select * from ${table} ${this.getWhere('schedule=-1')} ${this.orderBy(R)} ${this.getLimit(R)}`
        })();
    },
    /**
     * 获取一个任务的日志
     * 如果日志已经是 .ok 表示文件已经写好，可以写入到数据库
     * 如果日志后缀依旧没有 .ok 表示任务还在进行，想数据库随机写 schedule + 1 但是不能超过 100
     */
    getATaskLog(db,R) {
        let $this = this;
        db.query({
            table,
            sql: `select * from ${table} where id='${R.content.param.id}'`
        },((err,ret) => {
            if (err) {
                $this.defaultCallBack(R,err,{});
            } else {
                if (ret.length) {
                    ret = ret[0];
                    if (ret.schedule >= 100) {
                        // 执行完成
                        $this.defaultCallBack(R,null,ret);
                    } else {
                        if (fs.existsSync(ret.log)) {
                            let log = fs.readFileSync(ret.log,'utf-8');
                            if (ret.schedule < 99 && Math.random() < 0.5) {
                                ret.schedule = parseInt(log.split('\n').length / 13 * 100);
                                db.query({
                                    sql: `update ${table} set schedule='${ret.schedule}' where id='${ret.id}'`
                                });
                            }
                            $this.defaultCallBack(R,null,Object.assign({},ret,{ log }));
                        } else if (fs.existsSync(ret.log + '.ok')) {
                            // 已经执行完成，修改数据库的 schedule 和 log
                            let logFile = ret.log + '.ok';
                            // 这一步很关键
                            ret.log = fs.readFileSync(logFile,'utf-8');
                            ret.schedule = checkFail(ret);
                            ret.end_time = new Date().getTime();
                            // 写回数据库
                            db.query({
                                table,
                                sql: `update ${table} set schedule="100",log="${ret.log}",end_time="${ret.end_time}" where id="${ret.id}"`
                            },err => {
                                if (err) {
                                    // 失败，为什么？
                                    $this.defaultCallBack(R,err,{});
                                } else {
                                    // 成功，将日志发给前端并将 ok 文件删除
                                    fs.unlinkSync(logFile);
                                    $this.defaultCallBack(R,null,ret);
                                }
                            });
                        } else {
                            // todo 日志丢失怎么搞
                            $this.defaultCallBack(R,"日志丢失",{});
                        }
                    }
                } else {
                    $this.defaultCallBack(R,"找不到记录",{});
                }
            }
        }));
    },
});

/**
 * req、res 是请求时的两个对象
 * type 是请求类型 get 和 post
 * db 是数据库
 * */
module.exports = (db,requestType,req,res) => {
    let R = new RClass({
        req , res , requestType
    }).analizeReq();
    taskMethods.db = db;
    taskMethods.R = R;
    taskMethods.table = table;
    if (R.content.param.api in taskMethods) {
        taskMethods[R.content.param.api](db,R);
    } else {
        R.setError("接口不存在")
            .resCallBack();
    }
};